package com.gframework.mybatis.dao.mybatis.provider.core;

import java.lang.reflect.Field;
import java.util.Map;

import org.apache.ibatis.binding.BindingException;
import org.apache.ibatis.binding.MapperProxyFactory;
import org.apache.ibatis.binding.MapperRegistry;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.apache.ibatis.builder.annotation.MapperAnnotationBuilder;
import org.apache.ibatis.reflection.ReflectionException;
import org.apache.ibatis.session.Configuration;

/**
 * 扩展 MapperRegistry 类，增强addMapper方法。
 * <p>由于不同表的所有的dao都复用（继承）了相同的dao接口，有着相同的主键crud操作，因此无法通过Options注解去指定keyColumn和keyProperty，因此扩展此操作，通过解析泛型上的POJO类区设置keyColumn和keyProperty
 * 
 * @since 1.0.0
 * @author Ghwolf
 */
public class ExtendMapperRegistry extends MapperRegistry {

	/**
	 * MapperAnnotationBuilder 类的 assistant 成员对象
	 */
	private static final Field ASSISTANT_FIELD ;
	/**
	 * MapperBuilderAssistant 类的 resource 成员对象
	 */
	private static final Field RESOURCE_FIELD ;
	static {
		try {
			ASSISTANT_FIELD = MapperAnnotationBuilder.class.getDeclaredField("assistant");
			ASSISTANT_FIELD.setAccessible(true);
			
			RESOURCE_FIELD = MapperBuilderAssistant.class.getDeclaredField("resource");
			RESOURCE_FIELD.setAccessible(true);
		} catch (Exception e) {
			throw new ReflectionException("mybatis Configuration初始化异常",e);
		}
	}
	/**
	 * 这个是父类的knowMappers对象
	 */
	private final Map<Class<?>, MapperProxyFactory<?>> knownMappers ;
	private Configuration config ;
	
	@SuppressWarnings("unchecked")
	public ExtendMapperRegistry(Configuration config) {
		super(config);
		this.config = config ;
		Field f;
		try {
			f = MapperRegistry.class.getDeclaredField("knownMappers");
			f.setAccessible(true);
			this.knownMappers = (Map<Class<?>, MapperProxyFactory<?>>) f.get(this); 
		} catch (Exception e) {
			throw new ReflectionException("mybatis Configuration初始化异常，从MapperRegistry类获取knownMappers属性失败！",e);
		}
	}
	
	@Override
	public <T> void addMapper(Class<T> type) {
		if (type.isInterface()) {
			if (hasMapper(type)) {
				throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
			}
			boolean loadCompleted = false;
			try {
				knownMappers.put(type, new MapperProxyFactory<>(type));
				// It's important that the type is added before the parser is
				// run
				// otherwise the binding may automatically be attempted by the
				// mapper parser. If the type is already known, it won't try.
				MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
				this.changeAssistant(parser,type);	// 扩展操作
				parser.parse();
				loadCompleted = true;
			} finally {
				if (!loadCompleted) {
					knownMappers.remove(type);
				}
			}
		}
	}
	
	
	private void changeAssistant(MapperAnnotationBuilder parser,Class<?> type) {
		try {
			MapperBuilderAssistant assistant = (MapperBuilderAssistant) ASSISTANT_FIELD.get(parser);
			String source = (String) RESOURCE_FIELD.get(assistant);
			MapperBuilderAssistant extendAssistant = new ExtendMapperBuilderAssistant(this.config,source,type);
			ASSISTANT_FIELD.set(parser, extendAssistant);
		} catch(Exception e) {
			throw new ReflectionException("修改 MapperAnnotationBuilder 类的 assistant 成员失败！无法扩展keyProperty和keyColumn的获取功能。",e);
		}
	}

}
